package com.flyqiu.flow.core;

import com.flyqiu.common.tools.FlyQiuCollectionTools;
import com.flyqiu.flow.api.FlyQiuFlowConfig;
import com.flyqiu.flow.api.FlyQiuFlowEngine;
import com.flyqiu.flow.api.cache.FlyQiuFlowCacheManage;
import com.flyqiu.flow.api.candidate.FlyQiyFlowCandidateManage;
import com.flyqiu.flow.api.context.FlyQiuFlowGlobalContext;
import com.flyqiu.flow.api.deploy.FlyQiuFlowDeployManage;
import com.flyqiu.flow.api.deploy.IFlowDeploy;
import com.flyqiu.flow.api.event.FlowEngineEvent;
import com.flyqiu.flow.api.format.FlyQiuFlowBpmnFormatManage;
import com.flyqiu.flow.api.format.IBpmnFormat;
import com.flyqiu.flow.api.script.FlyQiuFlowScriptActuatorManage;
import com.flyqiu.flow.api.service.FlowHistoryService;
import com.flyqiu.flow.api.service.FlowModelService;
import com.flyqiu.flow.api.service.FlowRunningService;
import com.flyqiu.flow.api.service.FlowTaskService;
import com.flyqiu.flow.core.deploy.FullyAutomaticProcDeploy;
import com.flyqiu.flow.core.deploy.ManualProcDeploy;
import com.flyqiu.flow.core.event.FlyQiuFlowEngineEvent;
import com.flyqiu.flow.core.manage.DefaultFlyQiuFlowCacheManage;
import com.flyqiu.flow.core.script.DefaultJuelFlyQiuFlowScriptActuator;
import com.flyqiu.flow.event.ActInstEvent;
import com.flyqiu.flow.event.ProcInstEvent;
import com.flyqiu.flow.event.UserTaskEvent;
import com.flyqiu.flow.event.api.FlyQiuFlowEventManage;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Properties;

;

/**
 * 基础流程引擎
 */
@Slf4j
public abstract class BaseFlowEngineImpl implements FlyQiuFlowEngine {

    protected FlyQiuFlowConfig procConfig;

    protected FlyQiuFlowGlobalContext globalContext;

    protected FlowRunningService flowRunningService;
    protected FlowTaskService flowTaskService;
    protected FlowModelService modelService;
    protected FlowHistoryService flowHistoryService;

    private String versionCode = "1.0.0";

    protected static FlyQiuFlowEngineEvent defaultEngineEvent = new FlyQiuFlowEngineEvent();

    public void setProcConfig(FlyQiuFlowConfig procConfig) {
        this.procConfig = procConfig;
    }


    public BaseFlowEngineImpl() {
        Properties pr = new Properties();
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("flyqiu.flow.properties");
        try {
            pr.load(resourceAsStream);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        versionCode = pr.getProperty("version", versionCode);
    }


    /**
     * 初始化
     *
     * @param procConfig 流程配置
     */
    public void initEngine(FlyQiuFlowConfig procConfig) {
        if (this.globalContext == null) {
            throw new RuntimeException("globalContext is null");
        }
        if (procConfig != null) {
            this.procConfig = procConfig;
        }
        if (procConfig == null) {
            procConfig = new DefaultFlowConfigImpl();
        }

        FlyQiuFlowEventManage flowEventManage = globalContext.getFlowEventManage();
        flowEventManage.addEvent(FlowEngineEvent.class, defaultEngineEvent);
        log.debug("注册事件处理器");
        registerFlowEvents(flowEventManage, procConfig);
        flowEventManage.triggerCreateBeforeEvent(FlowEngineEvent.class, this);
        initCoreService();
        registerConfigComponents();
        flowEventManage.triggerCreateAfterEvent(FlowEngineEvent.class, this);
        flowEventManage.triggerCompleteBeforeEvent(FlowEngineEvent.class, this);
        flowEventManage.triggerCompleteAfterEvent(FlowEngineEvent.class, this);
    }


    protected void registerConfigComponents() {
        log.info("注册资源部署器");
        FlyQiuFlowDeployManage procDeployManage = globalContext.getFlowDeployManage();

        registerFlowDeploy(procDeployManage, globalContext);

        log.info("注册流程缓存器");
        FlyQiuFlowCacheManage procCacheManage = globalContext.getFlowCacheManage();
        registerFlowCaches(procCacheManage, procConfig);
        log.info("注册流程脚本处理器");
        FlyQiuFlowScriptActuatorManage scriptManage = globalContext.getFlowScriptManage();
        registerFlowScriptActuators(scriptManage, procConfig);

        log.info("注册流程模型格式化处理器");
        FlyQiuFlowBpmnFormatManage flowBpmnFormatManage = globalContext.getFlowBpmnFormatManage();
        registerBpmnFormat(flowBpmnFormatManage, procConfig);
        log.info("注册候选人处理器");
        FlyQiyFlowCandidateManage flowCandidateManage = globalContext.getFlowCandidateManage();
        registerCandidate(flowCandidateManage, procConfig);
    }

    protected void registerCandidate(FlyQiyFlowCandidateManage flowCandidateManage, FlyQiuFlowConfig procConfig) {
        FlyQiuCollectionTools.iterator(procConfig.getCandidateLoads(), flowCandidateManage::register);
    }


    /**
     * 注册流程事件
     *
     * @param eventManage
     * @param procConfig
     */
    protected void registerFlowEvents(FlyQiuFlowEventManage eventManage, FlyQiuFlowConfig procConfig) {
        List<FlowEngineEvent> procEngineEvents = procConfig.engineEvents();
        for (FlowEngineEvent event : procEngineEvents) {
            eventManage.addEvent(FlowEngineEvent.class, event);
        }
        List<ProcInstEvent> procInstEvents = procConfig.procInstEvents();
        for (ProcInstEvent event : procInstEvents) {
            eventManage.addEvent(ProcInstEvent.class, event);
        }
        List<ActInstEvent> actInstEvents = procConfig.activityEvents();
        for (ActInstEvent event : actInstEvents) {
            eventManage.addEvent(ActInstEvent.class, event);
        }
        List<UserTaskEvent> userTaskEvents = procConfig.userTaskEvents();
        for (UserTaskEvent event : userTaskEvents) {
            eventManage.addEvent(UserTaskEvent.class, event);
        }

    }


    protected void registerBpmnFormat(FlyQiuFlowBpmnFormatManage flyQiuFlowBpmnFormatManage, FlyQiuFlowConfig procConfig) {
        List<IBpmnFormat> bpmnFormats = procConfig.getBpmnFormat();
        FlyQiuCollectionTools.iterator(bpmnFormats, flyQiuFlowBpmnFormatManage::registerBpmnFormat);

    }

    protected FlyQiuFlowCacheManage initFlowCacheManage(FlyQiuFlowGlobalContext globalContext, FlyQiuFlowConfig procConfig) {
        return new DefaultFlyQiuFlowCacheManage();
    }

    protected void registerFlowCaches(FlyQiuFlowCacheManage flyQiuFlowCacheManage, FlyQiuFlowConfig procConfig) {
        FlyQiuCollectionTools.iterator(procConfig.getProcCaches(), flyQiuFlowCacheManage::registerCache);
    }


    protected void registerFlowScriptActuators(FlyQiuFlowScriptActuatorManage flyQiuFlowScriptActuatorManage, FlyQiuFlowConfig procConfig) {
        flyQiuFlowScriptActuatorManage.setDefaultScriptActuatorType(DefaultJuelFlyQiuFlowScriptActuator.JUEL_SCRIPT_TYPE);
        FlyQiuCollectionTools.iterator(procConfig.getScriptActuators(), flyQiuFlowScriptActuatorManage::register);
    }


    protected void registerFlowDeploy(FlyQiuFlowDeployManage manage, FlyQiuFlowGlobalContext context) {
        manage.registerProcDeploy(new ManualProcDeploy(context));
        manage.registerProcDeploy(new FullyAutomaticProcDeploy(context));
        List<IFlowDeploy> procDeploys = procConfig.procDeploys();
        FlyQiuCollectionTools.iterator(procDeploys, manage::registerProcDeploy);
    }


    @Override
    public FlyQiuFlowConfig getConfig() {
        return procConfig;
    }

    @Override
    public FlyQiuFlowGlobalContext getGlobalContext() {
        return globalContext;
    }

    /**
     * 初始化核心服务
     */
    protected abstract void initCoreService();


    /**
     * 获取历史服务
     *
     * @return FlowHistoryService
     */
    @Override
    public FlowHistoryService getHistoryService() {
        return flowHistoryService;
    }

    /**
     * 获取流程模型服务
     *
     * @return FlowModelService
     */
    @Override
    public FlowModelService getModelService() {
        return modelService;
    }

    /**
     * 获取流程实例服务
     *
     * @return FlowRunningService
     */
    @Override
    public FlowRunningService getFlowRunningService() {
        return flowRunningService;
    }

    /**
     * 获取任务服务
     *
     * @return FlowTaskService
     */
    @Override
    public FlowTaskService getTaskService() {
        return flowTaskService;
    }

    @Override
    public final String versionCode() {
        return versionCode;
    }

}
